using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.Web.UI;
using log4net;
using Sedna.Core.Controller.Configurations;
using Spring.Util;

namespace Sedna.Core.Controls.Support.Javascript
{
	public class JavascriptController
    {
        #region Constants
        private const string javascriptKey="coreims.javascript.";
		private const string commonFooterScriptKey="common-footer";
		private const string commonHeaderScriptKey="common-header";
        #endregion 

        #region Fields

        private static ILog log=LogManager.GetLogger(typeof(JavascriptController));

		private IJavascriptEmitterConfiguration configuration;
		private Page page;
		private IDictionary objects=new ListDictionary();
		private IList declaredClasses=new ArrayList(10);
	    
        #endregion

        #region Properties
	    public static JavascriptController Current
	    {
	        get
	        {
                return JavascriptEmitter.Current.Controller;
	        }
	    }
        #endregion


        public JavascriptController(Page page, IJavascriptEmitterConfiguration configuration)
		{
			this.configuration=configuration;
			this.page=page;
			IncludeCommon();
		}

//		public void RegisterHeaderScript(string key, string script)
//		{
//			page.ClientScript.RegisterClientScriptBlock(typeof(JavascriptController), key, script);
//		}
//
//		public void RegisterFooterScript(string key, string script)
//		{
//            page.ClientScript.RegisterStartupScript(typeof(JavascriptController), key, script);
//		}
        //
        #region Public methods
	    
        public void DeclareClass(string className)
		{
            if (StringUtils.IsNullOrEmpty(className))
            {
                throw new ArgumentNullException("className", "Class name cannot be null");
            }
			if (declaredClasses.Contains(className))
			{
				return;
			}
			declaredClasses.Add(className);
		}

		public void DeclareObject(string objectName, string className, bool encloseWithQuotes, params object[] parameters)
		{
            DeclareClass(className);
			if (objects.Contains(objectName))
			{
				return;
			}
			objects.Add(objectName, JavascriptUtil.ObjectDeclaration(objectName, className, encloseWithQuotes, parameters));			
		}

	    public void DeclareArray(string arrayName, params object[] parameters)
	    {
            if (objects.Contains(arrayName)) 
            {
                return;
            }
            objects.Add(arrayName, JavascriptUtil.ArrayDeclaration(arrayName, false, parameters));
        }
	    
		public void DeclareObject(string objectName, string className, params object[] parameters)
		{
			DeclareObject(objectName, className, false, parameters);
		}

	    public void DeclareObjectByNode(string objectName, string className, string nodeId)
	    {
            if (objects.Contains(objectName))
            {
                return;
            }
            objects.Add(objectName, JavascriptUtil.ObjectDeclarationByNode(objectName, nodeId));
	    }
	    
		public void DeclareObjectWithQuotedParams(string objectName, string className, params object[] parameters)
		{
			DeclareObject(objectName, className, true, parameters);
		}

	    public void RenderObjects(HtmlTextWriter writer)
		{
			ScriptTag classLoaderTag=new ScriptTag();
            classLoaderTag.Body = String.Format("ClassLoader.instance.batchLoad({0});", JavascriptUtil.ToScriptObject(declaredClasses));

			ScriptTag tag=new ScriptTag();
			tag.Body=GetControlDeclarations();

			page.ClientScript.RegisterStartupScript(typeof(JavascriptController), javascriptKey+"main", classLoaderTag.ToString()+'\n'+tag.ToString() );
        }

        #endregion

        #region Private methods
//
//        private string GetNode(string nodeId)
//	    {
//            return String.Format("document.getElementById(\"{0}\")", nodeId);
//	    }
//	    

		private string GetControlDeclarations()
		{
            StringBuilder sb = new StringBuilder();

			foreach (string declaration in objects.Values)
			{
			    string decl = declaration;
                //trim var to make this variables global. todo: change JavascriptUtils according to this
                if (decl.StartsWith("var "))
                {
                    decl = decl.Substring(4);
                }
			    sb.Insert(0, decl);
			    sb.Insert(0, '\n');
			}
		    sb.Insert(0, "\nfunction updateDeclarations(){\nFramework.create();");
            sb.Append("\n}\nupdateDeclarations();\n");
            sb.Append("function pageLoad(sender, args) {\nif(args.get_isPartialLoad()){\nupdateDeclarations();}\n}\n");
		    return sb.ToString();
		}

//		private string GetFrameworkDeclaration()
//		{
////			IList objectNames=new object[objects.Count];
////			int i=objects.Count-1;
////			foreach (object obj in objects.Keys)
////			{
////				objectNames[i--]=obj;
////			}
////			return String.Format("Framework.create({0});\n", new ScriptObjectGenerator(objectNames, false).ToScript());	
//			return String.Format("\nFramework.create();\n");	
//		}


		private string ResolveFileName(string scriptKey)
		{
			string fileName=null;
//			try 
//			{
            if (configuration.Classes!=null&&configuration.Classes[scriptKey]!= null)
		    {
			    fileName=((IJavascriptClassConfiguration)configuration.Classes[scriptKey]).FileName;
		    }
            if (fileName==null) fileName = scriptKey + ".js"; 
//			}
//			catch
//			{
//				string errorMessage="Unable to resolve javascript file name for scriptKey: \""+scriptKey+"\"";
//				log.Error(errorMessage);
//				throw new ControllerConfigurationException(errorMessage);	
//			}
			return page.ResolveUrl(configuration.ScriptBase+fileName);
		}

		private void IncludeCommon()
		{
            string commonClassesTags = String.Empty;
		    
		    foreach (string className in configuration.CommonClasses)
		    {
                ScriptTag classTag = new ScriptTag();
                classTag.Src = ResolveFileName(className);
                classTag.ScriptClass = className;
                commonClassesTags += classTag.ToString();
            }
		    
//			ScriptTag classLoaderClassTag=new ScriptTag();	
//			classLoaderClassTag.Src=ResolveFileName("ClassLoader");
//            classLoaderClassTag.ScriptClass="ClassLoader";

			ScriptTag headerScriptTag=new ScriptTag();
			headerScriptTag.Src=ResolveFileName(commonHeaderScriptKey);
	
			ScriptTag classLoaderDeclarationTag=new ScriptTag();
            classLoaderDeclarationTag.Body = String.Format("ClassLoader.create({0}, {1}, {2});\nFramework.create();", JavascriptUtil.EncloseWithQuotes(page.ResolveUrl(configuration.ScriptBase)), new ScriptObjectGenerator(configuration.Classes).ToScript(), new ScriptObjectGenerator(configuration.CommonClasses).ToScript());
			//classLoaderDeclarationTag.Body+=String.Format("ClassLoader.instance.batchLoad({0});", JavascriptUtil.ToScriptObject(configuration.CommonClasses));
	
			page.ClientScript.RegisterClientScriptBlock(typeof(JavascriptController), javascriptKey+"ClassLoader", headerScriptTag.ToString()+'\n'+commonClassesTags.ToString()+'\n'+classLoaderDeclarationTag.ToString());

			ScriptTag tag=new ScriptTag();
			tag.Src=ResolveFileName(commonFooterScriptKey);
            page.ClientScript.RegisterStartupScript(typeof(JavascriptController), commonFooterScriptKey, tag.ToString());
        }

        #endregion

    }
}
